#ifndef BAS_BAS_LOGGER_H
#define BAS_BAS_LOGGER_H

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

/**
 * 日志级别
 */
typedef enum
{
    LOGLEVEL_INFO,    ///<信息
    LOGLEVEL_FATAL,   ///<致命错误
    LOGLEVEL_ERROR,   ///<错误
    LOGLEVEL_WARN,    ///<警告
    LOGLEVEL_DEBUG    ///<调试
} E_LogLevel;

/**
 * 日志
 */
typedef struct T_Logger_tag
{
    E_LogLevel eLogLevel;         ///<日志级别, 大于该级别的日志不输出
    char       *caFilePath;       ///<日志文件路径
    FILE       *fpLogger;         ///<日志文件指针
    size_t     nChangeLogSize;    ///<日志文件切换的文件大小(单位MB)
    bool       bIsDailyChange;    ///<是否要日切换
    char       caLastChangeDate[8 + 1]; ///<上次切换日志的日期
} T_Logger;

/**
 * @brief     记录日志
 * @param[in] ptLogger       日志对象指针, 若为NULL, 使用默认日志
 * @param[in] iLogLevel      日志级别
 * @param[in] bIsPlainOutput 是否输出简单日志信息(无类似行号等附加信息,只输出用户给出的日志)
 * @param[in] caFileName     源代码文件名
 * @param[in] iLineCount     源代码行号
 * @param[in] caFunctionName 函数名
 * @param[in] caFormat       格式化字符串
 * @param[in] xxx            可变参数
 * @note      INFO级别不输出操作系统错误信息
 */
void basLoggerLog(T_Logger *ptLogger, E_LogLevel eLogLevel, const bool bIsPlainOutput,
                  const char *caFileName, const int iLineCount, const char *caFunctionName,
                  const char *caFormat, ...);

#define LOG_INFO(...)  basLoggerLog(NULL, LOGLEVEL_INFO, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_FATAL(...) basLoggerLog(NULL, LOGLEVEL_FATAL, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_ERROR(...) basLoggerLog(NULL, LOGLEVEL_ERROR, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_WARN(...)  basLoggerLog(NULL, LOGLEVEL_WARN, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG(...) basLoggerLog(NULL, LOGLEVEL_DEBUG, false, __FILE__, __LINE__, __func__, __VA_ARGS__)

#define LOG_INFO_PLAIN(...)  basLoggerLog(NULL, LOGLEVEL_INFO, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_FATAL_PLAIN(...) basLoggerLog(NULL, LOGLEVEL_FATAL, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_ERROR_PLAIN(...) basLoggerLog(NULL, LOGLEVEL_ERROR, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_WARN_PLAIN(...)  basLoggerLog(NULL, LOGLEVEL_WARN, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG_PLAIN(...) basLoggerLog(NULL, LOGLEVEL_DEBUG, true, __FILE__, __LINE__, __func__, __VA_ARGS__)

#define LOG_INFO_HANDLE(_ptLogger, ...)  basLoggerLog(_ptLogger, LOGLEVEL_INFO, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_FATAL_HANDLE(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_FATAL, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_ERROR_HANDLE(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_ERROR, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_WARN_HANDLE(_ptLogger, ...)  basLoggerLog(_ptLogger, LOGLEVEL_WARN, false, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG_HANDLE(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_DEBUG, false, __FILE__, __LINE__, __func__, __VA_ARGS__)

#define LOG_INFO_HANDLE_PLAIN(_ptLogger, ...)  basLoggerLog(_ptLogger, LOGLEVEL_INFO, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_FATAL_HANDLE_PLAIN(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_FATAL, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_ERROR_HANDLE_PLAIN(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_ERROR, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_WARN_HANDLE_PLAIN(_ptLogger, ...)  basLoggerLog(_ptLogger, LOGLEVEL_WARN, true, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG_HANDLE_PLAIN(_ptLogger, ...) basLoggerLog(_ptLogger, LOGLEVEL_DEBUG, true, __FILE__, __LINE__, __func__, __VA_ARGS__)

/**
 * @brief     初始化日志
 * @param[in] caFilePath      日志文件路径
 * @param[in] nChangeLogSize  日志文件切换的文件大小 
 * @param[in] bIsDailyChange  是否要日切换 
 * @returns   日志对象指针, 失败返回NULL 
 * @note      1.对于初始化得到的日志对象,其日志级别由环境变量LOGLEVEL控制
 *              LOGLEVEL=1 只输出INFO级别日志
 *              LOGLEVEL=2 只输出INFO,FATAL级别日志
 *              LOGLEVEL=3 只输出INFO,FATAL,ERROR级别日志
 *              LOGLEVEL=4 只输出INFO,FATAL,ERROR,WARN级别日志
 *              LOGLEVEL=5 输出所有日志信息
 *              若LOGLEVEL未设置, 则默认输出所有日志
 *            2.如果指定nChangeLogSize = 0, 则日志不会分文件存放
 *            3.如果指定nChangeLogSize > 0, 则当文件大小超过nChangeLogSize时, 
 *              会将旧文件重命名为caFilePath.YY_MM_DD_HH_MM_SS_XXX(XXX为毫秒)
 *              同时新建一个caFilePath文件,继续记录日志
 *            4.nChangeLogSize的单位是MB
 *            5.如果指定了bIsDailyChange = true, 则日期变换时会切换日志
 */
T_Logger *basLoggerInit(const char *caLogFilePath, const size_t nChangeLogSize, const bool bIsDailyChange);

/**
 * @brief     取日志级别
 * @param[in] ptLogger 日志对象指针
 * @returns   当前的日志级别
 */
E_LogLevel basLoggerGetLogLevel(T_Logger *ptLogger);

/**
 * @brief     设置日志级别
 * @param[in] ptLogger  日志对象指针
 * @param[in] iLogLevel 日志级别
 * @returns   原先的日志级别
 */
E_LogLevel basLoggerSetLogLevel(T_Logger *ptLogger, const E_LogLevel eLogLevel);

/**
 * @brief     关闭日志
 * @param[in] ptLogger 日志对象指针
 * @returns   void
 */
void basLoggerClose(T_Logger *ptLogger);

/**
 * @brief     取默认日志对象
 * @param[in] void
 * @returns   当前默认日志对象指针
 * @see       basLoggerSetDefault
 */
T_Logger *basLoggerGetDefault(void);

/**
 * @brief     设置默认日志
 * @param[in] ptLogger 日志对象指针
 * @returns   原先的默认日志对象指针
 * @note      如果未指定默认日志, 则使用标准错误作为默认日志
 * @see       basLoggerGetDefault
 */
T_Logger *basLoggerSetDefault(T_Logger *ptLogger);

/**
 * @brief     取日志对象的文件指针
 * @param[in] ptLogger 日志对象指针
 * @returns   日志对象的文件指针
 */
FILE *basLoggerGetFileHandle(T_Logger *ptLogger);

#endif
